package com.inspinia.auth.sso.server.controller;

import com.baomidou.kisso.*;
import com.baomidou.kisso.common.SSOProperties;
import com.baomidou.kisso.common.util.HttpUtil;
import com.baomidou.kisso.web.waf.request.WafRequestWrapper;
import com.inspinia.auth.common.util.AjaxHelper;
import com.inspinia.auth.rpc.api.AuthService;
import com.inspinia.base.util.JsonUtils;
import com.inspinia.base.util.MD5;
import com.inspinia.base.util.StringUtils;
import com.inspinia.config.common.config.SystemConfig;
import com.inspinia.upms.common.model.user.UpmsUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;

/**
 * @author Veblen
 */
@Controller
@RequestMapping("sso")
public class SsoController{

    private static Logger log = LoggerFactory.getLogger(SsoController.class);

    @Autowired
    private AuthService authService;

    /**
     * 跳转
     * @param url
     * @return
     */
    public String redirectTo(String url) {
        StringBuffer rto = new StringBuffer("redirect:");
        rto.append(url);
        return rto.toString();
    }

    /**
     * 登录页显示
     * @param request
     * @param model
     * @param kickout
     * @param response
     * @return
     * @throws IOException
     */
    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public String toLoginPage(HttpServletRequest request, Model model, String kickout, HttpServletResponse response) throws IOException {
        String returnUrl = request.getParameter(SSOConfig.getInstance().getParamReturl());
        if (StringUtils.isNotBlank(returnUrl)) {
            model.addAttribute("returnUrl", returnUrl);
        }

        Token token = SSOHelper.getToken(request);
        if (StringUtils.isNotBlank(kickout)) {
            model.addAttribute("kickout", kickout);
            SSOHelper.kickLogin(token.getUid());
            //登录页信息
            loadLoginSetting(model);
            return "login";
        }
        if (token == null) {
            //登录页信息
            loadLoginSetting(model);
            return "login";
        }
        return redirectTo(returnUrl);
    }

    /**
     * 登录 （注解跳过权限验证）
     */
    @RequestMapping("/login")
    public String login(Model model, HttpServletRequest request, HttpServletResponse response) {
        String returnUrl = request.getParameter(SSOConfig.getInstance().getParamReturl());
        Token token = SSOHelper.getToken(request);
        if (token == null) {
            /**
             * 正常登录 需要过滤sql及脚本注入
             */
            WafRequestWrapper wr = new WafRequestWrapper(request);
            String username = wr.getParameter("username");
            String password = wr.getParameter("password");
            boolean rememberMe = Boolean.parseBoolean(wr.getParameter("rememberMe"));

            if (StringUtils.isEmpty(username) || password == null) {
                model.addAttribute("message", "用户名或密码不能为空！");
                return "login";
            }

            UpmsUser user = authService.findUser(username, MD5.md5(password));

            if (Objects.nonNull(user)) {
                //进入系统内部
                if (StringUtils.isEmpty(returnUrl)) {
                    //回调地址为空并且时admin用户
                    if (user.getUserName().equals("admin")){
                        return "index";
                    }else{
                        //登录页信息
                        loadLoginSetting(model);
                        model.addAttribute("message","非系统管理员！");
                        return "login";
                    }
                } else {
                    //重定向到指定地址 returnUrl
                    //记住密码就设置
                    if (rememberMe) {
                        //30天免登
                        SSOConfig.getInstance().setCookieMaxage(604800);
                    }
                    SSOToken st = new SSOToken();
                    st.setId(request.getSession().getId());
                    st.setUid(user.getUserId().toString());
                    st.setData(JsonUtils.toJson(user));

                    // true 会销毁当前 JsessionId 如果用到了 session 相关改为 false
                    SSOHelper.setSSOCookie(request, response, st, false);
                    returnUrl = HttpUtil.decodeURL(returnUrl);
                }
                return redirectTo(returnUrl);
            }else{
                //登录页信息
                loadLoginSetting(model);
                model.addAttribute("message", "用户名或密码错误！");
                return "login";
            }
        } else {
            if (StringUtils.isNotBlank(returnUrl)){
                return redirectTo(returnUrl);
            }
            return "index";
        }
    }

    @ResponseBody
    @RequestMapping("/replylogin")
    public void replylogin(HttpServletRequest request, HttpServletResponse response) {
        log.info("sso server reply...");

        StringBuffer replyData = new StringBuffer();
        replyData.append(request.getParameter("callback")).append("({\"msg\":\"");
        SSOToken token = SSOHelper.getToken(request);
        if (token != null) {
            String askData = request.getParameter("askData");
            if (askData != null && !"".equals(askData)) {
                /**
                 *
                 * 用户自定义配置获取
                 *
                 * <p>
                 * 由于不确定性，kisso 提倡，用户自己定义配置。
                 * </p>
                 *
                 */
                SSOProperties prop = SSOConfig.getSSOProperties();

                //下面开始验证票据，签名新的票据每一步都必须有。
                AuthToken at = SSOHelper.replyCiphertext(request, askData);
                if (at != null) {

                    //1、业务系统公钥验证签名合法性（此处要支持多个跨域端，取 authToken 的 app 名找到对应系统公钥验证签名）
                    at = at.verify(prop.get("sso.defined." + at.getApp() + "_public_key"));
                    if (at != null) {

                        //at.getUuid() 作为 key 设置 authToken 至分布式缓存中，然后 sso 系统二次验证
                        //at.setData(data); 设置自定义信息，当然你也可以直接 at.setData(token.jsonToken()); 把当前 SSOToken 传过去。
                        //设置绑定用户ID
                        at.setUid(token.getUid());
                        //设置登录时间
                        at.setTime(token.getTime());
                        at.setId(token.getId());
                        at.setData(token.getData());
                        //2、SSO 的私钥签名
                        at.sign(prop.get("sso.defined.sso_private_key"));
                        //3、生成回复密文票据
                        replyData.append(at.encryptAuthToken());
                    } else {
                        //非法签名, 可以重定向至无权限界面，自己处理
                        replyData.append("-2");
                    }
                } else {
                    //非法签名, 可以重定向至无权限界面，自己处理
                    replyData.append("-2");
                }
            }
        } else {
            // 未登录
            replyData.append("-1");
        }
        try {
            replyData.append("\"})");
            AjaxHelper.outPrint(response, replyData.toString(), "UTF-8");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 统一退出，调用对外提供退出的所有接口
     */
    @RequestMapping(value = "/logout", method = RequestMethod.GET)
    public String logout(HttpServletRequest request, HttpServletResponse response, Model model) {
        loadLoginSetting(model);
        String returnUrl = request.getParameter(SSOConfig.getInstance().getParamReturl());
        if (StringUtils.isNotBlank(returnUrl)) {
            model.addAttribute("returnUrl", returnUrl);
        }
        SSOHelper.clearLogin(request, response);
        return "login";
    }

    /**
     * 登录页面信息
     * @param model
     */
    public static void loadLoginSetting(Model model) {
        model.addAttribute("panelTitle", SystemConfig.systemLoginPanelTitle);
        model.addAttribute("pageTitle", SystemConfig.systemLoginPageTitle);
        model.addAttribute("pageFoot", SystemConfig.systemPagefoot);
        model.addAttribute("version", SystemConfig.version);
        model.addAttribute("isShowLogo", SystemConfig.isShowLogo);
    }
}